
package com.mephi.gui;


import java.awt.Color;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import org.apache.log4j.Logger;
import com.mephi.interfaces.Chessboard;
import com.mephi.util.CastlingSort;
import com.mephi.util.Constants;
import com.mephi.util.PieceColor;
import com.mephi.util.PieceLoaderKey;
import com.mephi.util.PieceSort;

/**
 * Default implementation of Chessboard interface.
 * 
 * @author mephisto
 * 2011-07-27
 */
public class ChessboardImpl extends JPanel implements Chessboard
{
	public ChessboardImpl()
    {
		this(DEFAULT_FIELD_SIZE, DEFAULT_BRIGHT_FIELD_COLOR, DEFAULT_DARK_FIELD_COLOR); 	
    }
	
    public ChessboardImpl(int fieldSize, Color brightFieldColor, Color darkFieldColor)
    {
    	//super(400,600);
    	setFieldSize(fieldSize);
    	setBrightFieldColor(brightFieldColor);
    	setDarkFieldColor(darkFieldColor);
    	this.setSize(DEFAULT_BOARD_SIZE, DEFAULT_BOARD_SIZE);
    	//this.setSize(200, 200);
    	this.setBackground(Color.BLUE);
    	//this.setLayout(new FlowLayout());
    	
    	//container = getContentPane();
        //setBounds(100, 100, 470, 495);
        setBackground(new Color(204, 204, 204));
        //setDefaultCloseOperation(EXIT_ON_CLOSE);
        //setResizable(false);
        this.setLayout(null);     
        panBoard.setBounds(3, 3, 480, 480);
        panBoard.setBackground(new Color(255, 255, 255));

		this.add(panBoard);

        this.drawChessBoardWithPieces();

        //this.arrangeChessPieces();

        setVisible(true);
        
        
        Set<String> s = chessFieldsMap.keySet();
        for(String it : s)
        {
        	logger.debug(it + ": "+chessFieldsMap.get(it).getPiece());
        }
        
       /* Collection<Field> c = chessFieldsMap.values();
    	logger.debug("coll size: "+c.size());
    	for(Field it : c)
    		logger.debug(it.getPiece());*/
    	
    	
    	
    	/*panBoard = new JPanel();
    	panBoard.setSize(DEFAULT_BOARD_SIZE, DEFAULT_BOARD_SIZE);*/
    	
    	//panBoard.setLayout(new GridBagLayout());
    	//panBoard.add(new Field(8, brightFieldColor, null), new GBC(1,1).setFill(GBC.HORIZONTAL).setInsets(20));
    	
    	//add(new Field(60, brightFieldColor, null));
    	//add(new Field(60, darkFieldColor, null));
    	
    	//this.add(new Field(60, brightFieldColor, null), new GBC(1,1).setInsets(1));
    	//this.add(new Field(60, darkFieldColor, null), new GBC(2,1).setInsets(1));
    	//this.add(panBoard, new GBC(0,0));
    	
    	/*for (int x=0; x<8; x++)
    	{
    		for (int y=0; y<8; y++)
    		{
    			panBoard.add(new Field(8, brightFieldColor, null), new GBC(x,y).setFill(GBC.HORIZONTAL).setInsets(20));
    		}
    	}*/
    	//panBoard.add(new Field(), new GBC(0,1).setFill(GBC.HORIZONTAL).setInsets(20));
    }
    
    // This method draws chess board, i.e. black and white cells on the board
    private void drawChessBoardWithPieces()
	{
    	StringBuilder strField = new StringBuilder();
    	for (int x = 0; x < 8; x++)   
    		for (int y = 0; y < 8; y++)
        	{
        		strField.append(cols[y]).append(FIELDS_NUMBER-x);
        		logger.debug("Drawing: " + strField.toString());
        		
        		Piece tmp = (Piece) piecesMap.get(startingPositionMap.get(strField.toString()));
        		Piece piece = null;
        		if (tmp != null)
        			piece = (Piece) tmp.clone();
        		
        		if (y % 2 == 0)
        			if (x % 2 != 0)
        				chessFieldsMap.put(strField.toString().trim(), 
        						new Field(60, Color.DARK_GRAY, piece)); 				
        			else 
        				chessFieldsMap.put(strField.toString().trim(), 
        						new Field(60, Color.WHITE, piece));
        		else
			        if (x % 2 == 0)
			        	chessFieldsMap.put(strField.toString().trim(), 
			        			new Field(60, Color.DARK_GRAY, piece));
			        else
			        	chessFieldsMap.put(strField.toString().trim(), 
			        			new Field(60, Color.WHITE, piece));
        		
				panBoard.add(chessFieldsMap.get(strField.toString()));
				strField.delete(0, strField.length());
            }  	
	}
    
    /**
	 * 
	 * @param field the field to set
	 * @return the Piece on the field or null if there is no Piece
	 */
	@Override
	public Piece getPiece(String field)
	{
		return chessFieldsMap.get(field).getPiece();
	}

	/**
	 * 
	 * @param from the field from the Piece will be moved to set
	 * @param to the field to the Piece will be moved to set
	 */
	@Override
	public void movePiece(String from, String to)
	{
		Piece p = chessFieldsMap.get(from).getPiece();
		removePiece(from);
		chessFieldsMap.get(from).repaint();
		chessFieldsMap.get(to).setPiece(p);
		chessFieldsMap.get(to).repaint();
	}
	
	/**
	 * 
	 * @param from the field from the Piece will be moved to set
	 * @param to the field to the Piece will be moved to capture another Piece to set
	 */
	@Override
	public void capturePiece(String from, String to)
	{
		chessFieldsMap.get(to).getPiece().clearIcon();
		movePiece(from, to);
	}
	
	/**
	 * 
	 * @param from the field from the Piece will be moved to set
	 * @param to the field to the Piece will be moved to set
	 * @param pieceSort the PieceSort to which pawn will be promoted 
	 */
	@Override
	public void promotePawn(String from, String to, PieceSort pieceSort)
	{
		movePiece(from, to);
		chessFieldsMap.get(to).repaint();
		chessFieldsMap.get(to).getPiece().clearIcon();
		
		StringBuilder sB = null;
		if (to.endsWith("8"))
			sB = new StringBuilder("w");
		else if (to.endsWith("1"))
			sB = new StringBuilder("b");
		else
		{
			StringBuilder errMsg = new  StringBuilder("Promotion is impossible for movement: ")
				.append(pieceSort.getAsLetter()).append(from).append(Constants.PAUSE_STRING)
				.append(to).append(Constants.EXCLAMATION_MARK);
			JOptionPane.showMessageDialog(null, errMsg, "Invalid movement", JOptionPane.ERROR_MESSAGE);
			logger.error(errMsg);
			throw new IllegalStateException(errMsg.toString().trim());
		}
		
		sB.append(pieceSort.getAsLetter());
		PieceLoaderKey.valueOf("wQ");
		Piece tmp = piecesMap.get(PieceLoaderKey.valueOf(sB.toString().trim()));
		Piece p = (Piece) tmp.clone();
		setPiece(p, to);
		/*chessFieldsMap.get(to).setPiece(p);
		chessFieldsMap.get(to).repaint();*/
	}

	/**
	 * 
	 * Removes Piece from field indicates by field.
	 * 
	 * @param field the field from which Piece will be removed
	 */
	@Override
	public void removePiece(String field)
	{
		chessFieldsMap.get(field).setPiece(null);//.getPiece().clearIcon();
		chessFieldsMap.get(field).repaint();
	}

	/**
	 * 
	 * @param piece the piece to set
	 * @param field the field on which piece will be displayed
	 */
	@Override
	public void setPiece(Piece piece, String field)
	{
		chessFieldsMap.get(field).setPiece(piece);
		chessFieldsMap.get(field).repaint();
	}
	
    /**
	 * Makes long or short castling.
	 * 
	 * @param pieceColor the pieceColor to set
	 * @param castlingSort the castlingSort to set
	 */
	@Override
	public void makeCastling(PieceColor pieceColor, CastlingSort castlingSort)
	{
		logger.debug("Method makeCastling starts");
		if (pieceColor.equals(PieceColor.WHITE))
		{
			if (castlingSort.equals(CastlingSort.SHORT))
			{
				movePiece("e1", "g1");
				movePiece("h1", "f1");
			}
			else
			{
				movePiece("e1", "c1");
				movePiece("a1", "d1");
			}
		}
		else
		{
			if (castlingSort.equals(CastlingSort.SHORT))
			{
				movePiece("e8", "g8");
				movePiece("h8", "f8");
			}
			else
			{
				movePiece("e8", "c8");
				movePiece("a8", "d8");
			}
		}
		logger.debug("Method makeCastling ends");
	}
	
	/**
	 * 
	 * Clears the chessboard. 
	 */
	@Override
	public void clearChessboard()
	{
		logger.debug("Method clearChessboard starts");
		Set<String> set = chessFieldsMap.keySet();
		for (String it : set)
		{
			Piece p = chessFieldsMap.get(it).getPiece();
			if (p != null)
				p.clearIcon();
		}	
		repaint();
		logger.debug("Method clearChessboard ends");
	}

	/**
	 * 
	 * Sets the starting position.
	 */
	@Override
	public void setStartingPosition()
	{
		logger.debug("Method setStartingPosition starts");
		StringBuilder strField = new StringBuilder();
    	for (int x = 0; x < 8; x++)   
    		for (int y = 0; y < 8; y++)
        	{
        		strField.append(cols[y]).append(FIELDS_NUMBER-x);
        		logger.debug(strField.toString());
        		logger.debug("Drawing: " + strField.toString());
        		
        		Piece tmp = (Piece) piecesMap.get(startingPositionMap.get(strField.toString()));
        		Piece piece = null;
        		if (tmp != null)
        		{
        			piece = (Piece) tmp.clone();
        			chessFieldsMap.get(strField.toString().trim()).setPiece(piece);
        		}
        		chessFieldsMap.get(strField.toString().trim()).repaint();
				strField.delete(0, strField.length());
            }
    	repaint();
    	logger.debug("Method setStartingPosition ends");
	}
	
	/**
	 * Returns the map with Fields which fully representing state on the chessboard. Keys are the name of fields
	 * from "a1"-a8 to "h1-h8" of the chesboard for example "a1" or "h4".
	 * 
	 * @return the map with Fields
	 */
	public Map<String, Field> getChessFieldsMap()
	{
		return chessFieldsMap;
	}	
    
	public int getFieldSize()
	{
		return fieldSize;
	}

	public void setFieldSize(int fieldSize)
	{
		this.fieldSize = fieldSize;
	}
	
	public Color getDarkFieldColor()
	{
		return darkFieldColor;
	}

	public void setDarkFieldColor(Color darkFieldColor)
	{
		this.darkFieldColor = darkFieldColor;
	}

	public Color getBrightFieldColor()
	{
		return brightFieldColor;
	}

	public void setBrightFieldColor(Color brightFieldColor)
	{
		this.brightFieldColor = brightFieldColor;
	}
    
    private static final int FIELDS_NUMBER = 8;
    private static final int DEFAULT_FIELD_SIZE = (int)Toolkit.getDefaultToolkit().getScreenSize().getHeight()/15;
    private static final int DEFAULT_BOARD_SIZE = 8*DEFAULT_FIELD_SIZE;
    private static Color DEFAULT_DARK_FIELD_COLOR = Color.DARK_GRAY;
    private static Color DEFAULT_BRIGHT_FIELD_COLOR = Color.WHITE;
    private static Logger logger = Logger.getLogger("LOG");
    private static final long serialVersionUID = -3710028093207824654L;
    private int fieldSize;
    private Color darkFieldColor;
    private Color brightFieldColor;
    
    private Map<String, Field> chessFieldsMap = new HashMap<String, Field>(64);
    private static final String[] cols = {"a", "b", "c", "d", "e", "f", "g", "h"};
    private static Map<PieceLoaderKey, Piece> piecesMap = null;
    private static  Map<String, PieceLoaderKey> startingPositionMap;
    
    private JPanel panBoard = new JPanel(new GridLayout(8, 8));
    
    
    static
    {
    	piecesMap =  PiecesLoader.getPiecesMap("Stauton");
    	
    	startingPositionMap = new HashMap<String, PieceLoaderKey>(32); 
    	//white pieces
    	startingPositionMap.put("a1", PieceLoaderKey.wR);
    	startingPositionMap.put("b1", PieceLoaderKey.wN);
    	startingPositionMap.put("c1", PieceLoaderKey.wB);
    	startingPositionMap.put("d1", PieceLoaderKey.wQ);
    	startingPositionMap.put("e1", PieceLoaderKey.wK);
    	startingPositionMap.put("f1", PieceLoaderKey.wB);
    	startingPositionMap.put("g1", PieceLoaderKey.wN);
    	startingPositionMap.put("h1", PieceLoaderKey.wR);
    	
    	startingPositionMap.put("a2", PieceLoaderKey.wp);
    	startingPositionMap.put("b2", PieceLoaderKey.wp);
    	startingPositionMap.put("c2", PieceLoaderKey.wp);
    	startingPositionMap.put("d2", PieceLoaderKey.wp);
    	startingPositionMap.put("e2", PieceLoaderKey.wp);
    	startingPositionMap.put("f2", PieceLoaderKey.wp);
    	startingPositionMap.put("g2", PieceLoaderKey.wp);
    	startingPositionMap.put("h2", PieceLoaderKey.wp);
    	
    	//black pieces
    	startingPositionMap.put("a8", PieceLoaderKey.bR);
    	startingPositionMap.put("b8", PieceLoaderKey.bN);
    	startingPositionMap.put("c8", PieceLoaderKey.bB);
    	startingPositionMap.put("d8", PieceLoaderKey.bQ);
    	startingPositionMap.put("e8", PieceLoaderKey.bK);
    	startingPositionMap.put("f8", PieceLoaderKey.bB);
    	startingPositionMap.put("g8", PieceLoaderKey.bN);
    	startingPositionMap.put("h8", PieceLoaderKey.bR);
    	
    	startingPositionMap.put("a7", PieceLoaderKey.bp);
    	startingPositionMap.put("b7", PieceLoaderKey.bp);
    	startingPositionMap.put("c7", PieceLoaderKey.bp);
    	startingPositionMap.put("d7", PieceLoaderKey.bp);
    	startingPositionMap.put("e7", PieceLoaderKey.bp);
    	startingPositionMap.put("f7", PieceLoaderKey.bp);
    	startingPositionMap.put("g7", PieceLoaderKey.bp);
    	startingPositionMap.put("h7", PieceLoaderKey.bp);	
    }


    /*    @Override
	protected void paintComponent(Graphics g) 
    {
    	g.setColor(Color.DARK_GRAY);
        g.fillRect(0, 0, DEFAULT_BOARD_SIZE, DEFAULT_BOARD_SIZE);
        add(new Field(60, brightFieldColor, null), new GBC(1,1).setInsets(1));
        add(new Field(60, darkFieldColor, null), new GBC(2,1).setInsets(1));
        //g.setColor(brightFieldColor);
        for (int stripeX = 0, counter = 0; stripeX < getWidth(); stripeX += 2*fieldSize, ++counter) 
        {
        	if (counter < FIELDS_NUMBER/2)
	            for (int y = 0, row = 0, counterX = 0; y < getHeight(); y += fieldSize, ++row, ++counterX)
	            {
	            	if (counterX < FIELDS_NUMBER)
	            	{
	            		int x = (row % 2 == 0) ? stripeX : (stripeX + fieldSize);
	            		//this.add(new Field(60, brightFieldColor, null), new GBC(x+1,y+1).setInsets(1));
	                	//this.add(new Field(60, darkFieldColor, null), new GBC(x+2,y+2).setInsets(1));
	                	
		                //g.fillRect(x, y, fieldSize, fieldSize);
	            	}
	            }
        }
	        
	        
        g.setColor(darkFieldColor);
        g.fillRect(0, 0, DEFAULT_BOARD_SIZE, DEFAULT_BOARD_SIZE);
        g.setColor(brightFieldColor);
        for (int stripeX = 0, counter = 0; stripeX < getWidth(); stripeX += 2*fieldSize, ++counter) 
        {
        	if (counter < FIELDS_NUMBER/2)
	            for (int y = 0, row = 0, counterX = 0; y < getHeight(); y += fieldSize, ++row, ++counterX)
	            {
	            	if (counterX < FIELDS_NUMBER)
	            	{
	            		int x = (row % 2 == 0) ? stripeX : (stripeX + fieldSize);
		                g.fillRect(x, y, fieldSize, fieldSize);
	            	}
	            }
        }
    }*/
}
